package com.foreveross.mofang.datapersistence.infra.utils;

import java.io.IOException;
import java.net.URI;
import java.net.URL;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.concurrent.TimeUnit;

import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;

import org.apache.http.HttpEntity;
import org.apache.http.HttpHost;
import org.apache.http.HttpResponse;
import org.apache.http.NameValuePair;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.methods.HttpRequestBase;
import org.apache.http.conn.params.ConnRoutePNames;
import org.apache.http.conn.scheme.PlainSocketFactory;
import org.apache.http.conn.scheme.Scheme;
import org.apache.http.conn.scheme.SchemeRegistry;
import org.apache.http.conn.ssl.SSLSocketFactory;
import org.apache.http.cookie.Cookie;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.impl.conn.tsccm.ThreadSafeClientConnManager;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.params.CoreConnectionPNames;
import org.apache.http.protocol.HTTP;
import org.apache.http.util.EntityUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class HttpClientUtil {

	private static Logger logger = LoggerFactory.getLogger(HttpClientUtil.class);

	private DefaultHttpClient httpClient;

	private int maxConLifeTimeMs = 60000;
	private int defaultMaxConPerHost = 500;
	private int maxTotalConn = 1000;
	private int conTimeOutMs = 3000;
	private int soTimeOutMs = 3000;

	public static HttpClientUtil getInstance() throws Exception {
		return new HttpClientUtil(80, 443);
	}

	public static HttpClientUtil getInstance(int httpProt) throws Exception {
		return new HttpClientUtil(httpProt, 443);
	}

	public static HttpClientUtil getInstance(int httpProt, int SSLPort) throws Exception {
		return new HttpClientUtil(httpProt, SSLPort);
	}

	private HttpClientUtil(int httpProt, int SSLPort) throws Exception {
		SSLContext ctx = null;
		try {
			ctx = SSLContext.getInstance("TLS");
			X509TrustManager tm = new X509TrustManager() {
				public void checkClientTrusted(X509Certificate[] xcs,
						String string) throws CertificateException {
				}

				public void checkServerTrusted(X509Certificate[] xcs,
						String string) throws CertificateException {
				}

				public X509Certificate[] getAcceptedIssuers() {
					return null;
				}
			};
			
			ctx.init(null, new TrustManager[] { tm }, null);
		} catch (Exception e) {
			logger.error("创建httpclint对象出错：", e);
			throw e;
		}
		
		SSLSocketFactory ssf = new SSLSocketFactory(ctx, SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);
		SchemeRegistry schemeRegistry = new SchemeRegistry();
		schemeRegistry.register(new Scheme("http", httpProt, PlainSocketFactory.getSocketFactory()));
		schemeRegistry.register(new Scheme("https", SSLPort, ssf));

		ThreadSafeClientConnManager connectionManager = new ThreadSafeClientConnManager(
				schemeRegistry, maxConLifeTimeMs, TimeUnit.MILLISECONDS);
		connectionManager.setMaxTotal(maxTotalConn);
		connectionManager.setDefaultMaxPerRoute(defaultMaxConPerHost);
		httpClient = new DefaultHttpClient(connectionManager);
		httpClient.getParams().setParameter(CoreConnectionPNames.CONNECTION_TIMEOUT, conTimeOutMs);
		httpClient.getParams().setParameter(CoreConnectionPNames.SO_TIMEOUT,soTimeOutMs);
	}

	public Map<String, String> login(String url, Map<String, String> params, Map<String, String> headerMap, String encoding) {
		HttpResponse response = null;
		HttpEntity entity = null;
		Map<String, String> result = new HashMap<String, String>();
		

		try {
			response = getHttpResponse(url, headerMap, params, null, HTTP.UTF_8, Reqeust.POST);
			result.put("cookie", assemblyCookie(httpClient.getCookieStore().getCookies()));
			entity = response.getEntity(); // 获取响应实体
			if (null != entity) {
				result.put("content", EntityUtils.toString(entity, encoding));
			}

		} catch (Exception e) {
			logger.error(e.getMessage());
		} finally {
			try {
				EntityUtils.consume(entity);
			} catch (IOException e) {
				logger.error(e.getMessage());
			} finally {
				httpClientClose();
			}
		}

		return result;
	}
	
	public String execute(String url, String encoding) throws Exception {
		return execute(url, null, encoding, Reqeust.GET);
	}
	
	public String execute(String url, Map<String, String> params, Reqeust requestType) throws Exception {
		return execute(url, params, HTTP.UTF_8, requestType);
	}
	
	public String execute(String url, Map<String, String> params, String encoding, Reqeust requestType) throws Exception {
		HttpResponse response = getHttpResponse(url, null, params, null, encoding, requestType);
		String content = EntityUtils.toString(response.getEntity(), encoding);
		
		logger.debug("url {}获取请求内容为：{}", url, content);
		
		return content;
	}
	
	public String execute(String url, Map<String, String> params, Map<String, String> headers, String encoding, Reqeust requestType) throws Exception {
		HttpResponse response = getHttpResponse(url, headers, params, null, encoding, requestType);
		String content = EntityUtils.toString(response.getEntity(), encoding);
		
		logger.debug("url {}获取请求内容为：{}", url, content);
		
		return content;
	}

	public HttpResponse getHttpResponse(String url, Map<String, String> headers, Object params, String ParamType, String encoding, Reqeust requestType) throws Exception {
		HttpRequestBase request = null;
		
		if (requestType == Reqeust.POST) {
			request = getPost(url, params, encoding, ParamType);
		} else {
			request = getGet(url, params);
		}
		
		if (headers != null) {
			headers.putAll(getBaseHeader());
			setHeader(request, headers);
		} else {
			setHeader(request, getBaseHeader());
		}

		HttpResponse response = httpClient.execute(request);

		return response;
	}
	
	public void setProxy(HttpHost proxy) {
		httpClient.getParams().setParameter(ConnRoutePNames.DEFAULT_PROXY, proxy);
	}
	
	
	public DefaultHttpClient getHttpClient() {
		return httpClient;
	}
	
	public void httpClientClose() {
		httpClient.getConnectionManager().shutdown(); // 关闭连接,释放资源
	}
	
	// 组装cookie
	private String assemblyCookie(List<Cookie> cookies) {
		StringBuffer sbu = new StringBuffer();

		for (Cookie cookie : cookies) {
			sbu.append(cookie.getName()).append("=").append(cookie.getValue()).append(";");
		}

		if (sbu.length() > 0) {
			sbu.deleteCharAt(sbu.length() - 1);
		}

		return sbu.toString();
	}

	/**
	 * 获取post
	 * 
	 * @param url
	 * @param params
	 * @param encoding   
	 * @param ParamType  如：application/json
	 * @return
	 * @throws Exception 
	 */
	@SuppressWarnings("unchecked")
	private HttpPost getPost(String url, Object params, String encoding, String ParamType) throws Exception {
		List<NameValuePair> paramsList = new ArrayList<NameValuePair>();
		StringEntity uefEntity = null;
		HttpPost post = new HttpPost(url);
		
		if (params instanceof Map) {
			for (Entry<String, String> entry : ((Map<String, String>) params).entrySet()) {
				paramsList.add(new BasicNameValuePair(entry.getKey(), entry.getValue()));
			}
			
			post = new HttpPost(url);
			post.setEntity(new UrlEncodedFormEntity(paramsList));
		} else if (params instanceof String) {
			uefEntity = new StringEntity(params.toString());
			uefEntity.setContentEncoding(encoding);
			uefEntity.setContentType(ParamType);
			post = new HttpPost(url);
			post.setEntity(uefEntity);
		}
		
		return post;
	}
	
	/**
	 * 获取 get请求
	 * 
	 * @param requestUrl
	 * @param params
	 * @return
	 * @throws Exception
	 */
	@SuppressWarnings("unchecked")
	private HttpGet getGet(String requestUrl, Object params) throws Exception {
		StringBuilder sb = new StringBuilder(requestUrl);
		
		if(!(params == null || requestUrl.matches(".*\\?$"))){
			sb.append("?");
		}
		
		if (params instanceof Map) {
			for (Entry<String, String> entry : ((Map<String, String>) params).entrySet()) {
				sb.append(entry.getKey()).append("=").append(entry.getValue()).append("&");
			}
		} else if (params instanceof String){
			sb.append(params.toString());
		}
		
		
		URL url = new URL(sb.toString()); 
		URI uri = new URI(url.getProtocol(), url.getUserInfo(), url.getHost(), url.getPort(), url.getPath(), url.getQuery(), null);
		
		return new HttpGet(uri);
	}


	private void setHeader(HttpRequestBase request, Map<String, String> headers) {
		for (Entry<String, String> entry : headers.entrySet()) {
			request.addHeader(entry.getKey(), entry.getValue());
		}
	}

	private Map<String, String> getBaseHeader() {
		int versionMain = (int) (Math.random() * 10);
		int version = (int) (Math.random() * 10);

		Map<String, String> header = new HashMap<String, String>();
		header.put("User-Agent", "Mozilla/5.0 (Windows NT 5.2; rv:"
				+ versionMain + "." + version + ".1) Gecko/20100101 Firefox/"
				+ versionMain + "." + version + ".1");
		header.put("Connection", "close");

		return header;
	}
	
	public enum Reqeust {
		POST,GET;
	}

	public static void main(String[] args) throws Exception {
		HttpClientUtil client = HttpClientUtil.getInstance();
		String content =  client.execute("http://20140507.ip138.com/ic.asp", "gbk");
		System.out.println(content);
	}
}
